﻿/*
 * Tiff BlockBitmapFrame
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 */
using System;
using System.Windows;
//using System.Windows.Media;
//using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;
using Petra.Gis.Geometry.Image.Tiff;

namespace Petra.Gis.Geometry.Image
{
  /// <summary>
  /// provides file based image data input and output operations, like getPixels
  /// </summary>
  public class BlockBitmapFrame : BlockBitmapSource
  {

    private TiffCompression _fileDataCompression;
    private TiffReader _tiffReader;
    private TiffBitmapDecoder _decoder;


    #region constructors



    /// <summary>
    /// creates TiffBitmapFrame from decoder and ImageFileDirectory
    /// </summary>
    /// <param name="codec"></param>
    /// <param name="ifd"></param>
    internal BlockBitmapFrame(TiffBitmapDecoder decoder, TiffMetadata metadata)
      : base(metadata)
    {
      _decoder = decoder;
      _fileDataCompression = decoder.GetCompression(this);
    }



    #endregion constructors
    

    #region Freezable

    /// <summary>
    ///     Creates an instance of a BlockBitmapFrame.
    /// </summary>
    /// <returns>
    ///     The new instance.
    /// </returns>
    /// <remarks>
    ///     The default implementation uses reflection to create a new
    ///     instance of this type.  Derived classes may override this
    ///     method if they wish to provide a more performant
    ///     implementation, or if their type does not have a public
    ///     default constructor.
    /// </remarks>
    protected override Freezable CreateInstanceCore()
    {
      return (Freezable)Activator.CreateInstance(this.GetType());
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     When Freezable is cloned, WPF will make deep clones of all
    ///     writable, locally-set properties including expressions. The
    ///     property's base value is copied -- not the current value. WPF
    ///     skips read only DPs.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void CloneCore(Freezable source)
    {
      base.CloneCore(source);

      BlockBitmapFrame blkBitmapFrame = (BlockBitmapFrame)source;
      CopyCore(blkBitmapFrame, /*useCurrentValue*/ false, /*willBeFrozen*/ false);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     When a Freezable's "current value" is cloned, WPF will make
    ///     deep clones of the "current values" of all writable,
    ///     locally-set properties. This has the effect of resolving
    ///     expressions to their values. WPF skips read only DPs.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void CloneCurrentValueCore(Freezable source)
    {
      base.CloneCurrentValueCore(source);

      BlockBitmapFrame blkBitmapFrame = (BlockBitmapFrame)source;
      CopyCore(blkBitmapFrame, /*useCurrentValue*/ true, /*willBeFrozen*/ false);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     Freezable.GetAsFrozen is semantically equivalent to
    ///     Freezable.Clone().Freeze(), except that you can avoid copying
    ///     any portions of the Freezable graph which are already frozen.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void GetAsFrozenCore(Freezable source)
    {
      base.GetAsFrozenCore(source);

      BlockBitmapFrame blkBitmapFrame = (BlockBitmapFrame)source;
      CopyCore(blkBitmapFrame, /*useCurrentValue*/ false, /*willBeFrozen*/ true);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="source">
    ///     The original instance to copy data from.
    /// </param>
    /// <remarks>
    ///     Freezable.GetCurrentValueAsFrozen is semantically equivalent to
    ///     Freezable.CloneCurrentValue().Freeze(), except that WPF will
    ///     avoid copying any portions of the Freezable graph which are
    ///     already frozen.
    ///     
    ///     If you derive from this class and have additional non-DP state
    ///     that should be transfered to copies, you should override the
    ///     CopyCommon method.
    /// </remarks>
    protected sealed override void GetCurrentValueAsFrozenCore(Freezable source)
    {
      base.GetCurrentValueAsFrozenCore(source);

      BlockBitmapFrame blkBitmapFrame = (BlockBitmapFrame)source;
      CopyCore(blkBitmapFrame, /*useCurrentValue*/ true, /*willBeFrozen*/ true);
    }

    /// <summary>
    ///     Copies data into a cloned instance.
    /// </summary>
    /// <param name="original">
    ///     The original instance to copy data from.
    /// </param>
    /// <param name="useCurrentValue">
    ///     Whether or not to copy the current value of expressions, or the
    ///     expressions themselves.
    /// </param>
    /// <param name="willBeFrozen">
    ///     Indicates whether or not the clone will be frozen.  If the
    ///     clone will be immediately frozen, there is no need to clone
    ///     data that is already frozen, you can just share the instance.
    /// </param>
    /// <remarks>
    ///     Override this method if you have additional non-DP state that
    ///     should be transfered to clones.
    /// </remarks>
    protected virtual void CopyCore(BlockBitmapFrame original, bool useCurrentValue, bool willBeFrozen)
    {
    }

    #endregion Freezable


    #region public members


    public TiffCompression Compression
    {
      get { return _fileDataCompression; }
    }



    public TiffBitmapDecoder Decoder
    {
      get { return _decoder; }
    }

    #endregion public members


    #region private members

    private void prepareBlockDiagram()
    {
      //do some basic validations
      if (_metadata.PlanarConfiguration == PlanarConfigurationType.Separate && (_metadata.BlockOffsets.Length % _metadata.SamplesPerPixel) != 0)
        throw new ArgumentException("number of blocks must be modulo of SamplesPerPixel when planar type separate");

      int numOfBlocks;
      //determine num of blocks
      if (_metadata.PlanarConfiguration == PlanarConfigurationType.Separate)
        numOfBlocks = _metadata.BlockOffsets.Length / _metadata.SamplesPerPixel;
      else
        numOfBlocks = _metadata.BlockOffsets.Length;

      //_blocks = new BitmapBlock[_metadata.NumOfVerticalBlocks, _metadata.NumOfHorizontalBlocks];
      _blocks = new BitmapBlock[numOfBlocks];

      Int32Rect blockSize;
      int blockId;

      for (int y = 0; y < _metadata.NumOfVerticalBlocks; y++)
        for (int x = 0; x < _metadata.NumOfVerticalBlocks; x++)
        {
          blockId = (y * x) + x;
          //prepare block size
          blockSize = new Int32Rect();
          blockSize.X = x * _metadata.BlockWidth;
          blockSize.Y = x * _metadata.BlockHeight;
          blockSize.Width = ((blockSize.X + _metadata.BlockWidth) > _metadata.Width) ? ((blockSize.X + _metadata.BlockWidth) - _metadata.Width) : _metadata.BlockWidth;
          blockSize.Height = ((blockSize.Y + _metadata.BlockWidth) > _metadata.Width) ? ((blockSize.Y + _metadata.BlockWidth) - _metadata.Width) : _metadata.BlockWidth;

          //if (_tiffReader != null) //codec exists, prepare stream based existing image blocks
          if (_metadata.PlanarConfiguration == PlanarConfigurationType.Contignous)
            _blocks[blockId] = new ContigousBitmapBlock(this, blockSize, _tiffReader, _metadata, blockId);
          else
            _blocks[blockId] = new SeparatedBitmapBlock(this, blockSize, _tiffReader, _metadata, (blockId * _metadata.SamplesPerPixel) );
        }


    }


    #endregion private members

  }
}
